package sky.cin.framework.ioms.rpc;

import com.alibaba.fastjson.JSONObject;
import net.sf.cglib.proxy.MethodProxy;
import sky.cin.framework.config.ConfigFile;
import sky.cin.framework.ioms.common.entities.ServiceEndPoint;
import sky.cin.framework.ioms.rpc.annotations.RemoteInvokeServer;
import sky.cin.framework.network.HTTP;
import sky.cin.framework.network.dto.HttpServletRequest;
import sky.cin.framework.network.nio.ServerUtils;
import sky.cin.framework.sfoc.core.AnnotationApplicationContext;
import sky.cin.framework.sfoc.core.annotations.beans.Component;
import sky.cin.framework.sfoc.core.annotations.https.GetMapping;
import sky.cin.framework.sfoc.core.annotations.https.RequestParam;
import sky.cin.framework.sfoc.core.common.Context;
import sky.cin.framework.sfoc.core.common.utils.BeanUtils;
import sky.cin.framework.sfoc.core.interfaces.BeanInterfaceFactory;

import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.net.InetSocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;

@Component
public class RpcInterfaceFactory implements BeanInterfaceFactory {
    
    private static String registryHost;
    private static Integer registryPort;
    private static Integer localPort;
    private static boolean init;
    private static Selector selector;
    private static SocketChannel registryChannel;
    private static Map<String, Map<String, SocketChannel>> rpcMap = new HashMap<>();

    @Override
    public boolean beforeProxyCreat(Class<?> clazz) {
        return BeanUtils.isAnnotationPresent(clazz, RemoteInvokeServer.class);
    }
    
    @Override
    public Object proxyCreated(Object bean) {
        return bean;
    }
    
    private static final Random RANDOM = new Random();
    
    @Override
    public Object intercept(Object bean, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        init();
        Class<?> rpcInterface = resolveSourceClass(bean);
        // 解析目标服务器，获取目标路径
        SocketChannel targetChannel = resolveTargetDomain(rpcInterface);
        if (targetChannel == null) {
            return null;
        }
        // 解析被调用方法的参数列表
        Class<?>[] parameterTypes = Arrays.stream(args).map(Object::getClass).toArray(Class<?>[]::new);
        Method sourceMethod = rpcInterface.getMethod(method.getName(), parameterTypes);
        // 获取请求路径
        String path = sourceMethod.getAnnotation(GetMapping.class).value();
        // 解析参数
        Annotation[][] parameterAnnotations = sourceMethod.getParameterAnnotations();
        // 开始请求
        String url = resolveArgs(parameterAnnotations, args, path);
        String result = invoke(targetChannel, url);
        // 数据封装
        Class<?> returnType = method.getReturnType();
        return JSONObject.parseObject(result, returnType);
    }

    private void init() throws Exception {
        if (init) {
            return;
        }
        ConfigFile config = Context.getApplicationConfig();
        registryHost = config.getString("registry.host");
        registryPort = config.getInteger("registry.port");
        localPort = config.getInteger("server.port");

        selector = Selector.open();
        registryChannel = bind(registryHost, registryPort);
        init = true;
    }

    private SocketChannel bind(ServiceEndPoint endPoint) {
        return bind(endPoint.getHost(), endPoint.getPort());
    }

    private SocketChannel bind(String host, Integer port) {
        try {
            SocketChannel channel = SocketChannel.open(new InetSocketAddress(host, port));
            channel.configureBlocking(false);
            channel.register(selector, SelectionKey.OP_READ);
            return channel;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    private SocketChannel resolveTargetDomain(Class<?> sourceClass) {
        try {
            // 生成请求
            String serviceName = sourceClass.getAnnotation(RemoteInvokeServer.class).value();
            String url = String.format("/registry/query?name=%s", serviceName);
            String data = invoke(registryChannel, url);
            ServiceEndPoint endPoint = JSONObject.parseObject(data, ServiceEndPoint.class);
            if (endPoint == null) {
                throw new RuntimeException(String.format("未找到的服务器: %s", serviceName));
            } else {
                String addr = String.format("%s:%d", endPoint.getHost(), endPoint.getPort());
                return rpcMap
                        .computeIfAbsent(serviceName, k -> new HashMap<>())
                        .computeIfAbsent(addr, k -> bind(endPoint));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    private String resolveArgs(Annotation[][] parameters, Object[] args, String path) {
        StringBuilder url = new StringBuilder(path);
        int index = 0;
        for (Annotation[] perParameter : parameters) {
            for (Annotation parameter : perParameter) {
                if (RequestParam.class.isAssignableFrom(parameter.getClass())) {
                    RequestParam requestParam = (RequestParam) parameter;
                    String key = requestParam.value();
                    Object value = args[index];
                    url.append(index == 0 ? "?" : "&");
                    url.append(key).append("=").append(value.toString());
                }
            }
            index++;
        }
        return url.toString();
    }
    
    private String invoke(SocketChannel client, String url) {
        HttpServletRequest request = new HttpServletRequest(url);
        request.setHost(HTTP.LOCAL_HOST, localPort);
        return invoke(client, request);
    }

    private String invoke(SocketChannel client, HttpServletRequest request) {
        return ServerUtils.doInvoke(client, request, selector).getData();
    }

}
